#include <string>
#include <assert.h>
namespace lty
{
    class string
    {
    public:
        //string(const char* str=nullptr)//不行，cout无法输出空指针的.c_str
        //string(const char* str='\0')
        //string(const char* str="\0")
        string(const char* str="")//全缺省初始化 避免空str无法被输出和崩溃
        :_size(strlen(str))
        {
            _capacity=_size==0 ? 3 : _size*2; //防止空间大小为0导致双倍 扩容失败
            _str=new char[_capacity+1];//为_str扩容初始化
            strcpy(_str,str);
        }
        //s2(s1)深拷贝
        string(const string& s)
        :_size(s._size)
        ,_capacity(s._capacity)
        {
            _str=new char[s._capacity+1];
            strcpy(_str,s._str);
        }
        //s2=s1深拷贝重载
        string& operator=(const string& s)
        {
//            if(this!=&s)
//            {
//                _size = s._size;
//                _capacity = s._capacity;
//                _str = new char[s._capacity + 1];
//                strcpy(_str, s._str);
//            }
//                return *this;

            if(this != &s)
            {
                //如果开空间失败不会影响this._str
                char* tmp = new char[s._capacity + 1];
                strcpy(tmp, s._str);//再把拷贝值拷贝进tmp中
                delete[] _str;//释放原来的值，如果不用tmp的话，无法释放之前的地址空间
                _str = tmp; //再指向tmp的地址
                _size = s._size;
                _capacity = s._capacity;
            }
            return *this;
        }
        ~string()
        {
            delete[] _str;
            _str= nullptr;
            _size=_capacity=0;
        }
        //在前后都加上const，返回值无法修改且this中的值也无法修改！
        const char& operator[](size_t pos) const//给const成员调用
        {
            assert(pos<_size);
            return _str[pos];
        }
        //函数重载
        char& operator[](size_t pos)    //给普通用户调用
        {
            assert(pos<_size);
            return _str[pos];
        }
        const char* c_str()
        {
            return _str;
        }

        //const成员调用非const函数是权限放大，普通用户调用是const权限缩小
        size_t size() const //后面加const只有在类成员函数中修饰*this，无法改变this中的值
        {
            return _size;
        }

        //迭代器底层实现
        typedef char* iterator;
        iterator begin()
        {
            return _str;//首地址
        }
        iterator end()
        {
            return _str+_size;//末地址
        }
        //const迭代器实现
        //const迭代器只能读 但是const_iterator类型变量可以变 例如++it
        typedef const char* const_iterator;
        const_iterator begin() const
        {
            return _str;//首地址
        }
        const_iterator end() const
        {
            return _str+_size;//末地址
        }

        //string比较大小重载
        //数据不改变的重载最好加上const，让const成员也能用
        bool operator>(const string& s) const
        {
            return strcmp(_str,s._str)>0;
        }
        bool operator==(const string& s) const
        {
            return strcmp(_str,s._str)==0;
        }
        bool operator>=(const string& s) const
        {
            return *this >s || *this ==s;   //复用
        }
        bool operator<(const string& s) const
        {
            return !(*this >=s);
        }
        bool operator<=(const string& s) const
        {
            return *this <s || *this ==s;
        }
        bool operator!=(const string& s) const
        {
            return !(*this==s);
        }


        //插入字符
        void reserve(size_t n)//扩容 容量小于现有大小，不改变容量
        {
            if(n>_capacity)//防止缩容
            {
                char* tmp=new char[n+1];
                strcpy(tmp,_str);
                delete[] _str;
                _str=tmp;
                _capacity=n;
            }
        }
        void push_back(char ch)//增加一个字符
        {
            if(_size+1>_capacity)
            {
                reserve(_capacity*2);//双倍扩容
            }
            _str[_size]=ch;//_size是最后'\0'位置
            ++_size;
            _str[_size]='\0';
        }
        void append(const char* str)
        {
            size_t len=strlen(str);
            if(_size+len>_capacity)
            {
                reserve(_size+len);
            }
            strcpy(_str+_size,str);
            _size=_size+len;
        }

        string& operator+=(char ch)
        {
            push_back(ch);
            return *this;
        }
        string& operator+=(const char* str)
        {
            append(str);
            return *this;
        }

        void resize(size_t n,char ch='\0')
        {
            if(n<=_size)//resize的大小小于原有的长度，直接屏蔽后面
            {
                _size=n;
                _str[_size]='\0';
            }
            else
            {
                if(n>_capacity)
                {
                    reserve(n);
                }
                size_t i=_size;
                while(i<n)
                {
                    _str[i]=ch;
                    ++i;
                }
                _size=n;
                _str[_size]='\0';//末尾加上'\0'结束
            }
        }

        string& insert(size_t pos,char ch)
        {
            assert(pos<=_size);
            if(_size+1>_capacity)
            {
                reserve(_capacity*2);
            }
            size_t end=_size+1;
            while(end > pos)//无符号0-1时会变成最大值，且又因为无符号和整形相比较会发生整形提升，所以用这种方式
            {
                _str[end]=_str[end-1];//往后挪
                --end;
            }
            _str[pos]=ch;
            ++_size;

            return *this;
        }

        string& insert(size_t pos,char* str)
        {
            assert(pos<=_size);
            size_t len=strlen(str);
            if(_size+len>_capacity)//扩容
            {
                reserve(_size+len);
            }
            size_t end=_size+len;
            while(end>pos+(len-1))//len-1和下标位置对齐
            {
                _str[end]=_str[end-len];//挪动数据
                --end;
            }
            strncpy(_str+pos,str,len);//在pos位置插入str中len个字符，不插入'\0'
            _size+=len;

            return *this;
        }

        //删除下标位置len个字符
        string& erase(size_t pos,size_t len=npos)
        {
            assert(pos<_size);
            if(len == npos || pos+len >= _size)
            {
                _str[pos]='\0';
                _size=pos;
            }
            else
            {
                strcpy(_str+pos,_str+pos+len);
                _size-=len;
            }
            return *this;
        }

        void swap(string& s)
        {
            std::swap(_str,s._str);
            std::swap(_size,s._size);
            std::swap(_capacity,s._capacity);
        }

        //找字符下标
        size_t find(char ch, size_t pos = 0)
        {
            assert(pos < _size);
            for (size_t i = pos; i < _size; ++i)
            {
                if (_str[i] == ch)
                {
                    return i;
                }
            }

            return npos;
        }

        //char *strstr(const char *haystack, const char *needle)
        //haystack -- 要被检索的 C 字符串。
        //needle -- 在 haystack 字符串内要搜索的小字符串。
        size_t find(const char* str,size_t pos=0)
        {
            assert(pos < _size);
            char* p=strstr(_str+pos,str);//找到str的起始地址
            if(p== nullptr)
            {
                return npos;
            }
            else
            {
                return p - _str;//str起始地址减去_str首地址就是第几个
            }
        }

        void clear()
        {
            _str[0] = '\0';
            _size = 0;
        }

    private:
        char* _str;
        size_t _size;
        size_t _capacity;
        static const size_t npos;
        //可以初始化，只针对整型，double等类型不行
//        static const size_t npos=-1;
    };
    const size_t string::npos=-1;
    ostream& operator<<(ostream& out,const string& s)
    {
        for(auto ch:s)
        {
            out << ch;
        }
        return out;
    }
    istream& operator>>(istream& in,string& s)
    {
        s.clear();//清空后_size=0，插入时插入的位置是_size位置
        char ch=in.get();//get()获取字符到流in中
        char buff[128];//防止一直扩容，直接存入buff数组中，一次性插入
        size_t i=0;
        while( ch != ' ' && ch != '\n' )
        {
            buff[i++]=ch;
            if(i==127)
            {
                buff[127]='\0';
                s+=buff;  //+=重载中，会在插入字符后在末尾插入一个'\0'
                i=0;
            }
            ch=in.get();
        }
        if(i!=0)//不够127个字符时
        {
            buff[i]='\0';
            s+=buff;
        }
        return in;
    }
}
